package sha_go

import (
	"encoding/binary"
)

// SHA1Context 结构体，用于保存SHA-1算法的中间状态
type SHA1Context struct {
	H      [5]uint32
	W      [80]uint32
	LenW   int
	SizeHi uint32
	SizeLo uint32
}

// SHA1Init 初始化SHA-1上下文
func SHA1Init(ctx *SHA1Context) {
	ctx.H[0] = 0x67452301
	ctx.H[1] = 0xEFCDAB89
	ctx.H[2] = 0x98BADCFE
	ctx.H[3] = 0x10325476
	ctx.H[4] = 0xC3D2E1F0
	ctx.LenW = 0
	ctx.SizeHi = 0
	ctx.SizeLo = 0
}

// SHA1Append 将数据追加到当前的消息块中
func SHA1Append(ctx *SHA1Context, dataIn []byte) {
	for _, b := range dataIn {
		ctx.W[ctx.LenW/4] <<= 8
		ctx.W[ctx.LenW/4] |= uint32(b)
		ctx.LenW++
		if ctx.LenW%64 == 0 {
			SHA1HashBlock(ctx)
			ctx.LenW = 0
		}
		ctx.SizeLo += 8
		if ctx.SizeLo < 8 {
			ctx.SizeHi++
		}
	}
}

// SHA1Finish 完成哈希计算，生成最终的哈希值
func SHA1Finish(ctx *SHA1Context) [20]byte {
	var hashout [20]byte
	pad0x80 := []byte{0x80}
	pad0x00 := []byte{0x00}
	padLen := make([]byte, 8)

	binary.BigEndian.PutUint32(padLen[0:4], ctx.SizeHi)
	binary.BigEndian.PutUint32(padLen[4:8], ctx.SizeLo)

	SHA1Append(ctx, pad0x80)
	for ctx.LenW != 56 {
		SHA1Append(ctx, pad0x00)
	}
	SHA1Append(ctx, padLen)

	for i := 0; i < 20; i++ {
		hashout[i] = byte(ctx.H[i/4] >> (24 - (i%4)*8))
	}

	// 重新初始化上下文
	SHA1Init(ctx)
	return hashout
}

// SHA1Hash 一次性计算给定数据的SHA-1哈希值
func SHA1Hash(dataIn []byte) [20]byte {
	ctx := SHA1Context{}
	SHA1Init(&ctx)
	SHA1Append(&ctx, dataIn)
	return SHA1Finish(&ctx)
}

// SHA1HashBlock 处理一个完整的消息块（64字节）
func SHA1HashBlock(ctx *SHA1Context) {
	var A, B, C, D, E, TEMP uint32

	for t := 16; t <= 79; t++ {
		ctx.W[t] = SHA_ROTL(ctx.W[t-3]^ctx.W[t-8]^ctx.W[t-14]^ctx.W[t-16], 1)
	}

	A = ctx.H[0]
	B = ctx.H[1]
	C = ctx.H[2]
	D = ctx.H[3]
	E = ctx.H[4]

	for t := 0; t <= 19; t++ {
		TEMP = SHA_ROTL(A, 5) + ((B & C) | (^B & D)) + E + ctx.W[t] + 0x5A827999
		E = D
		D = C
		C = SHA_ROTL(B, 30)
		B = A
		A = TEMP
	}
	for t := 20; t <= 39; t++ {
		TEMP = SHA_ROTL(A, 5) + (B ^ C ^ D) + E + ctx.W[t] + 0x6ED9EBA1
		E = D
		D = C
		C = SHA_ROTL(B, 30)
		B = A
		A = TEMP
	}
	for t := 40; t <= 59; t++ {
		TEMP = SHA_ROTL(A, 5) + ((B & C) | (B & D) | (C & D)) + E + ctx.W[t] + 0x8F1BBCDC
		E = D
		D = C
		C = SHA_ROTL(B, 30)
		B = A
		A = TEMP
	}
	for t := 60; t <= 79; t++ {
		TEMP = SHA_ROTL(A, 5) + (B ^ C ^ D) + E + ctx.W[t] + 0xCA62C1D6
		E = D
		D = C
		C = SHA_ROTL(B, 30)
		B = A
		A = TEMP
	}

	ctx.H[0] += A
	ctx.H[1] += B
	ctx.H[2] += C
	ctx.H[3] += D
	ctx.H[4] += E
}

// SHA_ROTL 循环左移操作
func SHA_ROTL(X uint32, n uint) uint32 {
	return ((X << n) | (X >> (32 - n))) & 0xFFFFFFFF
}
